################################################################################
# File: common.py
# Package: itacqos.uddi.views
# Author: Vincent Pretre
# Last update: $LastChangedDate: 2009-02-28 16:25:51 +0000 (Sat, 28 Feb 2009) $
################################################################################

## @package itacqos.uddi.views.common
# This module provides a set of functions used in views. It does not contain any view.
# All non-view functions of the views package are placed here

from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response, get_object_or_404, get_list_or_404 
from django.template.defaultfilters import slugify

from uddi.models import Category, Keyword, WebService, Vendor, Operation, TestCategory, Verdict, Test

import Image, ImageDraw
import os
import urllib

## The base url of the site (http://www.itacqos.org/demo/ for example)
#
# It must finish with a slash
base_url = 'http://localhost:8000/uddi/'

## Files repository (models, wsdl, tests ...)
#
# In this folder, a subfolder will be created for each registered service
service_directory = 'C:\\Documents and Settings\\Vincent\\Bureau\\itacqos\\uddi\\services\\'

## Removes every spaces in beginning and end of a string 
# @param k The word which will be cleaned
# @return The cleaned word
#
# Temporary function, should be updated with a good old regexp
def cleanKW(k):
	onlySpaces = True
	for c in k:
		if not c == ' ':
			onlySpaces = False
	
	if not onlySpaces:
		while k[-1] == ' ':
			k = k[:-1]
	
		while k[0] == ' ':
			k = k[1:]
	else:
		k = ''

	return k

## This function is used as render for all views of iTaC-QoS
# @param template Name of the template to use
# @param dict A dictionnary which contains every variable that have to be passed to the template
# @param request The request given to the view
# @return The page to display, using render_to_response
#
# This render automatically fetches some informations needed by the site menu, such as categories and keywords.
def newRender(template, dict, request):
	categories = {}
	for cat in Category.objects.all():
		categories[cat] = len(cat.webservice_set.filter(public = True))
	
	keywords = {}
	for kw in Keyword.objects.all().order_by('-webservice').distinct()[:10]:
		keywords[kw] = len(kw.webservice_set.filter(public = True))
	
	dict['menuCategories'] = categories
	dict['menuKeywords'] = keywords
	
	dict['base_url'] = base_url
	if request.user.is_authenticated():
		dict['user'] = request.user

	return render_to_response('uddi/' + template, dict)

## Computes pool of dependencies of an operation
# @param op The operation for which pool of dependencies has to be computed
# @return The pool of dependencies, in a list format
#
# This function computes pools recursively.
def getDependencies(op):
	list = [op]
		
	for o in op.reliesOn.all():
		subDep = getDependencies(o)
		
		for o1 in subDep:
			if o1 not in list:
				list.append(o1)
	
	return list
	
## Fetch user from a request
# @param request The request given to a view
# @return A dictionnary with useful informations (see below)
#
# Checks if a user is logged and returns a dictionnary { 'isAuthenticated' : boolean,
#                                                                            'user' : auth.User,
#                                                                            'isVendor' : boolean,
#                                                                            'vendor' : uddi.models.Vendor}
def getUser(request):
	res = {}
	if request.user.is_authenticated():
		res = {'isAuthenticated' : True, 'user' : request.user, 'isVendor' : False, 'vendor' : None}
		if request.user.vendor_set.count() > 0:
			res['isVendor'] = True
			res['vendor'] = Vendor.objects.get(user = request.user)
	else:
		res =  {'isAuthenticated' : False, 'user' : None, 'isVendor' : False, 'vendor' : None}
	
	return res

## Computes marks of a web service
# @param ws The web service for which the mark has to be computed
# @param unitary A boolean telling if the it computes unitary or relation mark. By default True (unitary mark)
# @returns A dictionnary containing the mark (see belox)
#
# The dictionnary describing a mark is quite complex, it looks like this:
# { operation 1  =>	 { FIA  =>  {	 passed => [ number of tests, percentage of tests ] ,
#					incomplete passed  => [ number of tests, percentage of tests ] ,
#					safe failed => [ number of tests, percentage of tests ] ,
#					... },
#			FDP  =>  { ... },
#			FPR => { ... },
#			FCT => { ... } },
#   second operation  => { ... } } \n
# (if you see this from Doxygen, take a look at the code, the dictionnary is displayed in a more fancy way ;) )
def getMark(ws, unitary=True):
	# We get all existing categories and verdict to simplify code
	verdicts = Verdict.objects.all()
	
	mark = {}
	# Browes all operations of the service
	for op in ws.operation_set.all():
		# Creates the sub-dictionnary
		mark[op] = {}

		# If its not a unitary mark, we have to compute dependencies of the operation
		if not unitary:
			dependencies = getDependencies(op)

		# Browses all test categories
		for testCat in TestCategory.objects.all():
			# A sub-sub-dictionnary is created (did I say the structure was a bit complex ? )
			mark[op][testCat] = {}
			
			# Fetch all the tests linked to the category
			tests = Test.objects.filter(category = testCat)
			
			# Filter the tests, treatment depends on the type of mark
			if unitary:
				tests = tests.filter(operation = op)
			else:
				tests = tests.filter(category = testCat).filter(operation__in=dependencies)
				
			# The number of tests is used to compute percentages
			nbTests = len(tests)
			
			# Browse all possible verdicts
			for verdict in verdicts:
				# Yes, a sub-sub-sub-dictionnary (well ok, it's a list)
				mark[op][testCat][verdict] = []
				
				# Computes the number of tests for this operation, category and verdict
				nbTestForVerdict = len(tests.filter(verdict = verdict))
				
				# Computes percentage
				if nbTests > 0:
					prcTestForVerdict = (100 * nbTestForVerdict) / nbTests
				else:
					prcTestForVerdict = 0
				
				# Data are added to the dictionnary
				mark[op][testCat][verdict].append(nbTestForVerdict)
				mark[op][testCat][verdict].append(prcTestForVerdict)
				
	return mark

## Gets a list of services from a category or a keyword
# @param container The category or keyword
# @param nbpp The number of services shown by page
# @param page The page asked
# @returns A dictionnary containing:
# - nbServices - the total number of services linked to this container
# - start/end  - number of the first/last service returned
# - pages - list of total pages
# - currentPage - the current page
# - services - the list of selected services
def getServicesByCatOrKeyword(container, nbpp, page):
	dict = {}
	
	nbServices = len(container.webservice_set.filter(public = True))
	dict['nbServices'] = nbServices
	
	start = nbpp * (page - 1)
	end = start + nbpp
	
	if (start > nbServices):
		raise Http404
	
	if (end > nbServices):
		end = nbServices
	
	dict['start'] = start
	dict['end'] = end

	dict['pages'] = range(1, (nbServices/nbpp + 2))
	dict['currentPage'] = page
	
	dict['services'] = container.webservice_set.filter(public = True)[start:end]
	
	return dict
	

## Creates a PNG image which sums up validation result of a web service.
# @param validation An array of five integer given percentage of tests for each verdict (passed, incomplete passed, safe failed, failed and critical failed)
# @param height Height of the image produced
# @param widthFactor By default the image produced has a width of 100px, the factor is used to change that
# @param colors, an array telling which color is used for each verdict. Last color is used to fill the image if all values of validation are equal to zero
# @return The image
def validationImg(validation, height = 10, widthFactor = 1,\
		      colors = [(0, 255, 0, 178), (139, 255, 0, 178), (255, 255, 0, 178), (255, 0, 0, 178), (0, 0, 0, 178), (165, 165, 165, 178)]):

	# Images creation
	img = Image.new("RGBA", (int(100 * widthFactor), height), (0, 0, 0, 0))
	draw = ImageDraw.Draw(img)

	start = 0
	i = 0
	allEmpty = True
	# Images drawing
	for v in validation:
		draw.line((start, 0, (start + int(v * widthFactor)), 0), fill = colors[i], width = (height * 2)) 
		start = start + int(v * widthFactor)
		i = i + 1
		if not v == 0:
			allEmpty = False
			
	if allEmpty:
		draw.line((0, 0, int(100 * widthFactor), 0), fill = colors[i], width = (height * 2))

	return img

## Creates a folder which is used to store files linked to a ws (xmi model, wsdl file, tests ...)
# @param ws The web service for which the folder is created
#
# This function updates the "dir" member of the web service and saves it.
def createWSFolder(ws):
	wsDir = service_directory+ str(ws.id)
	os.mkdir(wsDir)
	
	ws.dir = wsDir
	ws.save()

## Saves WSDL file of a web service
# @param ws The web service for which the WSDL has to be fetch
# @param update Tells is the previous WSDL file has to be deleted (False by default)
def saveWSDL(ws, update = False):
	path = ws.dir + '/' + ws.name + '.wsdl'
	
	if update and os.path.exists(path):
		os.remove(path)

	urllib.urlretrieve(ws.wsdl, path)

## Saves the model provided by the vendor
# @param request The request given to the view, needed because it contains the file uploaded
# @param update Tells if the previous model has to be deleted (False by default)
def saveModel(request, ws, update = False):
	f = request.FILES['xmiModel']
	path = ws.dir + '/model.xmi'
	
	if update and os.path.exists(path):
		os.remove(path)
	
	destination = open(path, 'wb+')
	
	for chunk in f.chunks():
		destination.write(chunk)

## Updates keywords of a web service
# @param ws The web service to update
# @param keywords A string containing all keywords, separated by a comma
#
# Creates keyword if needed, and links ws and keywords. Saves the web service.
def updateKW(ws, keywords):
	kws = keywords.split(",")
	
	# First, all keywords linked to the service are deleted (yes, it's a lazy way to do updates ;) )
	for kw in ws.keywords.all():
		ws.keywords.remove(kw)
	
	for kw in kws:
		kw = cleanKW(kw)
		kwSlug = slugify(kw)
		
		if Keyword.objects.filter(name=kw).count() == 0:
			kword = Keyword(name = kw, slug = kwSlug)
			kword.save()
		else:
			kword = Keyword.objects.get(name=kw)
	
		ws.keywords.add(kword)
	
	ws.save()
